package io.github.kevinzhwl.edgetts.api.impl;

import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import io.github.kevinzhwl.edgetts.api.EdgeTTSService;
import io.github.kevinzhwl.edgetts.bean.CommunicateRequest;
import io.github.kevinzhwl.edgetts.bean.VoiceItem;
import io.github.kevinzhwl.edgetts.error.EdgeErrorException;
import io.github.kevinzhwl.edgetts.util.CommunicateUtil;
import io.github.kevinzhwl.edgetts.util.TTSWssClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import static io.github.kevinzhwl.edgetts.constant.EdgeApiUrlConstants.TextToSpeech.*;


@RequiredArgsConstructor
@Slf4j
public class EdgeTTSServiceImpl implements EdgeTTSService {

  protected String invokeGet(String uri, Map<String, String> headers) throws EdgeErrorException {
    String responseContent = "";
    CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
    try {
      // Start the client
      httpclient.start();
      // Execute request
      final HttpGet request1 = new HttpGet(uri);
      if (headers != null && !headers.isEmpty()) {
        for (Map.Entry<String, String> e : headers.entrySet()) {
          request1.addHeader(e.getKey(), e.getValue());
        }
      }
      Future<HttpResponse> future = httpclient.execute(request1, null);
      // and wait until a response is received
      HttpResponse response1 = future.get();
      responseContent = EntityUtils.toString(response1.getEntity());

    } catch (ExecutionException | InterruptedException | IOException e) {
      throw new EdgeErrorException(e);
    } finally {
      try {
        httpclient.close();
      } catch (IOException e) {
        throw new EdgeErrorException(e);
      }
    }
    return responseContent;
  }

  @Override
  public List<VoiceItem> listVoices() throws EdgeErrorException {

    final String uri = String.format(VOICE_LIST_URL, TRUSTED_CLIENT_TOKEN);
    Map<String, String> headers = CommunicateUtil.getVoiceListHttpHeaders();
    final String responseContent = invokeGet(uri, headers);

    List<VoiceItem> voices = new ArrayList<>();
    voices = new GsonBuilder().create().fromJson(responseContent, new TypeToken<List<VoiceItem>>() {
    }.getType());

    return voices;
  }

  @Override
  public boolean communicate(String text, String savedFile, String voiceName) throws EdgeErrorException {
    CommunicateRequest req = new CommunicateRequest();
    req.setVoiceName(voiceName);
    return this.communicate(text, savedFile, req);
  }

  @Override
  public boolean communicate(String content, String savedFile, CommunicateRequest request) throws EdgeErrorException {


    final String contentClear = CommunicateUtil.removeIncompatibleCharacters(content);
    if (StringUtils.isBlank(contentClear)) {
      throw new EdgeErrorException("invalid content");
    }
    final String voiceName = CommunicateUtil.mkVoiceInfo(request.getVoiceName());


    final String dateStr = CommunicateUtil.dateToString(new Date());
    final String connectId = CommunicateUtil.connectId();

    final String audioFormat = CommunicateUtil.mkAudioFormat(dateStr, request.getFormat());
    final String ssml = CommunicateUtil.mkSsml(voiceName, request.getVoiceRate(), request.getVoiceVolume(), contentClear);
    final String ssmlHeadersPlusData = CommunicateUtil.mkSsmlHeadersPlusData(connectId, dateStr, ssml);

    Map<String, String> headers = new HashMap<>(CommunicateUtil.getCommunicateHttpHeaders());
    try {
      final String uri = String.format(COMMUNICATE_WSS_URL, TRUSTED_CLIENT_TOKEN, connectId);
      TTSWssClient client = new TTSWssClient(uri, headers, savedFile);
      client.connect();
      while (!client.isOpen()) {
        // wait open
        Thread.sleep(100);
      }
      client.send(audioFormat);
      client.send(ssmlHeadersPlusData);
      while (client.isOpen()) {
        // wait close
        Thread.sleep(1);
      }
      return true;
    } catch (URISyntaxException | InterruptedException e) {
      throw new EdgeErrorException(e);
    }
  }


}
